package ru.kest.babymonitor.service.audio;

public class JADPCMCodec {

    public static class JADPCMState {
        public long prevsample;
        public int previndex;
    }

//    private static final int MULTIPLIER = 20;
    /**
	 * Table of index changes
	 */
	final int[] IndexTable = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1,
			-1, -1, 2, 4, 6, 8 };

	/**
	 * Quantizer step size lookup table
	 */
	final long[] StepSizeTable = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21,
			23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97,
			107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337,
			371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166,
			1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
			3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,
			10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385,
			24623, 27086, 29794, 32767 };
	/**
	 * Difference between sample and predicted sample
	 */
	long diff;

	/**
	 * Quantizer step size
	 */
	long step;

	/**
	 * Output of ADPCM predictor
	 */
	long predsample;

	/**
	 * Dequantized predicted difference
	 */
	long diffq;

	/**
	 * Index into step size table
	 */
	int index;

	/*****************************************************************************
	 * 
	 ADPCMEncoder - ADPCM encoder routine
	 * 
	 ****************************************************************************** 
	 * 
	 * Input Variables:
	 * 
	 * 
	 * signed long sample - 16-bit signed speech sample
	 * 
	 * 
	 * Return Variable:
	 * 
	 * 
	 * char - 8-bit number containing the 4-bit ADPCM code
	 * 
	 *****************************************************************************/

	/**
	 * ADPCM encoder routine
	 * 
	 * @param state
	 *            encoder state
	 * @param sample
	 *            16-bit signed speech sample
	 * @return 8-bit number containing the 4-bit ADPCM code
	 */
	public byte encode(JADPCMState state, long sample) {
		/* ADPCM output value */
		int code;

		/* Temporary step size */
		long tempstep;

		/*
		 * Restore previous values of predicted sample and quantizer step size
		 * index
		 */
		predsample = state.prevsample;
		index = state.previndex;
		step = StepSizeTable[index];

		/*
		 * Compute the difference between the actual sample (sample) and the the
		 * predicted sample (predsample)
		 */
		diff = sample - predsample;
		if (diff >= 0) {
			code = 0;
		} else {
			code = 8;
			diff = -diff;
		}

		/*
		 * Quantize the difference into the 4-bit ADPCM code using the the
		 * quantizer step size
		 */
		tempstep = step;
		if (diff >= tempstep) {
			code |= 4;
			diff -= tempstep;
		}
		tempstep >>= 1;

		if (diff >= tempstep) {
			code |= 2;
			diff -= tempstep;
		}

		tempstep >>= 1;

		if (diff >= tempstep) {
			code |= 1;
		}

		/*
		 * Inverse quantize the ADPCM code into a predicted difference using the
		 * quantizer step size
		 */
		diffq = step >> 3;
		if ((code & 4) == 4)
			diffq += step;
		if ((code & 2) == 2)
			diffq += step >> 1;
		if ((code & 1) == 1)
			diffq += step >> 2;

		/*
		 * Fixed predictor computes new predicted sample by adding the old
		 * predicted sample to predicted difference
		 */
		if ((code & 8) == 8)
			predsample -= diffq;
		else
			predsample += diffq;

		/*
		 * Check for overflow of the new predicted sample
		 */
		if (predsample > 32768)
			predsample = 32768;
		else if (predsample < -32767)
			predsample = -32767;

		/*
		 * Find new quantizer stepsize index by adding the old index to a table
		 * lookup using the ADPCM code
		 */
		index += IndexTable[code];

		/*
		 * Check for overflow of the new quantizer step size index
		 */
		if (index < 0)
			index = 0;
		if (index > 88)
			index = 88;

		/*
		 * Save the predicted sample and quantizer step size index for next
		 * iteration
		 */
		state.prevsample = predsample;
		state.previndex = index;
		/* Return the new ADPCM code */
		return (byte) (code & 0x0f);
	}

	/**
	 * ADPCMEncoder - ADPCM encoder routine
	 * 
	 * @param state
	 *            encoder state
	 * @param code
	 *            char code - 8-bit number containing the 4-bit ADPCM code
	 * @return 16-bit signed speech sample
	 */
	public long decode(JADPCMState state, int code, int multiplier) {

		/*
		 * Restore previous values of predicted sample and quantizer step size
		 * index
		 */
		predsample = state.prevsample;
		index = state.previndex;

		/*
		 * Find quantizer step size from lookup table using index
		 */
		step = StepSizeTable[index];

		/*
		 * Inverse quantize the ADPCM code into a difference using the quantizer
		 * step size
		 */
		diffq = step >> 3;
		if ((code & 4) == 4)
			diffq += step;
		if ((code & 2) == 2)
			diffq += step >> 1;
		if ((code & 1) == 1)
			diffq += step >> 2;

		/*
		 * Add the difference to the predicted sample
		 */
		if ((code & 8) == 8)
			predsample -= diffq;
		else
			predsample += diffq;

		/*
		 * Check for overflow of the new predicted sample
		 */
		if (predsample > 32768)
			predsample = 32768;
		else if (predsample < -32767)
			predsample = -32767;

		/*
		 * Find new quantizer step size by adding the old index and a table
		 * lookup using the ADPCM code
		 */
		index += IndexTable[code];

		/*
		 * Check for overflow of the new quantizer step size index
		 */
		if (index < 0)
			index = 0;
		if (index > 88)
			index = 88;

		/*
		 * Save predicted sample and quantizer step size index for next
		 * iteration
		 */
		state.prevsample = predsample;
		state.previndex = index;
		/* Return the new speech sample */

        long result = Math.abs(predsample) * multiplier;

//        if (result < 500) {
//            result = 0;
//        }
        if (result > 15000) {
            result = (result - 15000) / 2 + 15000;
        }
        if (result > 20000) {
            result = (result - 20000) / 3 + 20000;
        }
        if (result > 27000) {
            result = (result - 27000) / 4 + 27000;
        }
        if (result > 32767) {
            result = 32766;
            state.previndex = 0;
            state.prevsample = 0;
        }
        if (predsample < 0) result = -result;
		return result;
	}

}
